/*
 * Copyright (c) 2004-2007 Apple Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
  Implementation of the weak / associative references for non-GC mode.
*/


#include "objc-private.h"
#include <objc/message.h>
#include <map>
#include "DenseMapExtras.h"

// expanded policy bits.

enum {
    OBJC_ASSOCIATION_SETTER_ASSIGN      = 0,
    OBJC_ASSOCIATION_SETTER_RETAIN      = 1,
    OBJC_ASSOCIATION_SETTER_COPY        = 3,            // NOTE:  both bits are set, so we can simply test 1 bit in releaseValue below.
    OBJC_ASSOCIATION_GETTER_READ        = (0 << 8),
    OBJC_ASSOCIATION_GETTER_RETAIN      = (1 << 8),
    OBJC_ASSOCIATION_GETTER_AUTORELEASE = (2 << 8),
    OBJC_ASSOCIATION_SYSTEM_OBJECT      = _OBJC_ASSOCIATION_SYSTEM_OBJECT, // 1 << 16
};

spinlock_t AssociationsManagerLock;

namespace objc {

class ObjcAssociation {
    uintptr_t _policy;
    id _value;
public:
    ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
    ObjcAssociation() : _policy(0), _value(nil) {}
    ObjcAssociation(const ObjcAssociation &other) = default;
    ObjcAssociation &operator=(const ObjcAssociation &other) = default;
    ObjcAssociation(ObjcAssociation &&other) : ObjcAssociation() {
        swap(other);
    }

    inline void swap(ObjcAssociation &other) {
        std::swap(_policy, other._policy);
        std::swap(_value, other._value);
    }

    inline uintptr_t policy() const { return _policy; }
    inline id value() const { return _value; }

    // 如果策略支持retain/copy，对value执行对应的操作
    inline void acquireValue() {
        if (_value) {
            switch (_policy & 0xFF) {
            case OBJC_ASSOCIATION_SETTER_RETAIN:
                _value = objc_retain(_value);
                break;
            case OBJC_ASSOCIATION_SETTER_COPY:
                _value = ((id(*)(id, SEL))objc_msgSend)(_value, @selector(copy));
                break;
            }
        }
    }

    inline void releaseHeldValue() {
        if (_value && (_policy & OBJC_ASSOCIATION_SETTER_RETAIN)) {
            objc_release(_value);
        }
    }

    // 如果policy支持retain，就对value进行retain操作
    inline void retainReturnedValue() {
        if (_value && (_policy & OBJC_ASSOCIATION_GETTER_RETAIN)) {
            objc_retain(_value);
        }
    }

    // 如果policy支持autorelease，就对value进行autorelease操作
    inline id autoreleaseReturnedValue() {
        if (slowpath(_value && (_policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE))) {
            return objc_autorelease(_value);
        }
        return _value;
    }
};

typedef DenseMap<const void *, ObjcAssociation> ObjectAssociationMap;
typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;

// class AssociationsManager manages a lock / hash table singleton pair.
// Allocating an instance acquires the lock
// 关联表：
class AssociationsManager {
    using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>;
    static Storage _mapStorage;         // 静态属性

public:
    AssociationsManager()   { AssociationsManagerLock.lock(); }     // 构造函数加锁
    ~AssociationsManager()  { AssociationsManagerLock.unlock(); }   // 析构函数解锁

    // 获取静态属性中的Map
    AssociationsHashMap &get() {
        return _mapStorage.get();
    }

    static void init() {
        _mapStorage.init();
    }
};

AssociationsManager::Storage AssociationsManager::_mapStorage;

} // namespace objc

using namespace objc;

// 初始化关联表
void _objc_associations_init() {
    AssociationsManager::init();
}

// 从全局索引表associations获取object中关联key对应的值
id _object_get_associative_reference(id object, const void *key) {
    ObjcAssociation association{};      // 创建一个空的ObjcAssociation

    {
        // 获取全局索引表associations
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.get());
        
        // 在associations根据object来寻找对应Map
        AssociationsHashMap::iterator i = associations.find((objc_object *)object);
        if (i != associations.end()) {  // 找到了
            // 获取Map
            ObjectAssociationMap &refs = i->second;
            // 在Map中根据key来查找ObjcAssociation
            ObjectAssociationMap::iterator j = refs.find(key);
            if (j != refs.end()) {      // 找到了
                association = j->second;
                association.retainReturnedValue();      // 如果policy支持retain，就对value进行retain操作
            }
        }
    }

    // 如果policy支持autorelease，就对value进行autorelease操作
    return association.autoreleaseReturnedValue();
}

// 设置：关联对象
void _object_set_associative_reference(id object, const void *key, id value, uintptr_t policy) {
    // This code used to work when nil was passed for object and key. Some code
    // probably relies on that to not crash. Check and handle it explicitly.
    // rdar://problem/44094390
    if (!object && !value) return;

    // 是否不允许使用关联对象，在rw.flags里面有一个位
    if (object->getIsa()->forbidsAssociatedObjects())
        _objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));

    // 转换objct的指针
    DisguisedPtr<objc_object> disguised{(objc_object *)object};
    // 创建一个ObjcAssociation
    ObjcAssociation association{policy, value};

    // 如果策略支持retain/copy，对value执行对应的操作
    // retain the new value (if any) outside the lock.
    association.acquireValue();

    bool isFirstAssociation = false;
    {
        // 关联策略管理者
        AssociationsManager manager;
        // 获取全局MapAssociationsHashMap
        AssociationsHashMap &associations(manager.get());

        if (value) {
            // 如果disguised在散列表associations中，那么refs_result = 散列表中的值；
            // 不在，把刚刚创建的空ObjectAssociationMap插入到散列表中，result = 这个ObjectAssociationMap
            auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
            if (refs_result.second) {
                /* it's the first association we make 这是第一个 */
                isFirstAssociation = true;
            }

            /* 获取值，refs也还是一个散列表 establish or replace the association */
            auto &refs = refs_result.first->second;
            // 如果key在算列表refs中，result = 散列表中的值；不在，把association插入到散列表中，result = association
            auto result = refs.try_emplace(key, std::move(association));
            // 判断second 是否 == nil
            if (!result.second) {
                // second == nil，说明没有存储值，把association存储到second
                association.swap(result.first->second);
            }
        } else {        // value == nil，从Map中移除key
            // 从associations查找objct对应的Map
            auto refs_it = associations.find(disguised);
            // 能找到Map
            if (refs_it != associations.end()) {
                // 获取Map：refs
                auto &refs = refs_it->second;
                // 从Map中查找key
                auto it = refs.find(key);
                if (it != refs.end()) {
                    // 把it->second替换成association -> {poliy, nil}
                    association.swap(it->second);
                    // 把it从refs移除
                    refs.erase(it);
                    // 如果refs已经空了，那么把refs_it从最外面的HashMap中移除
                    if (refs.size() == 0) {
                        associations.erase(refs_it);
                    }
                }
            }
        }
    }

    // Call setHasAssociatedObjects outside the lock, since this
    // will call the object's _noteAssociatedObjects method if it
    // has one, and this may trigger +initialize which might do
    // arbitrary stuff, including setting more associated objects.
    if (isFirstAssociation)
        // 如果是第一次关联，那么在isa中设置已经关联过
        object->setHasAssociatedObjects();

    // 对之前存储的oldValue进行release
    // release the old value (outside of the lock).
    association.releaseHeldValue();
}

// 把object从全局HashMap中进行释放，
// 如果对象没有deallocating，那么不能释放系统设置的管理值，
// 如果正在deallocating，那么系统设置的关联值最后释放
// Unlike setting/getting an associated reference,
// this function is performance sensitive because of
// raw isa objects (such as OS Objects) that can't track
// whether they have associated objects.
void _object_remove_assocations(id object, bool deallocating) {
    ObjectAssociationMap refs{};

    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.get());
        AssociationsHashMap::iterator i = associations.find((objc_object *)object);
        if (i != associations.end()) {      // 找到了
            // 把Map与空refs交换
            refs.swap(i->second);

            // If we are not deallocating, then SYSTEM_OBJECT associations are preserved.
            bool didReInsert = false;
            if (!deallocating) {
                for (auto &ref: refs) {
                    // 只有系统的管理值，需要重新放回去，我们的设置的关联值不会进入这个if
                    if (ref.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
                        i->second.insert(ref);      // 重新插入到Map中
                        didReInsert = true;
                    }
                }
            }
            // 如果Map中的关联值没有系统设置的，那么从HashMap中移除object对应的Map
            if (!didReInsert)
                associations.erase(i);
        }
    }

    // Associations to be released after the normal ones.
    SmallVector<ObjcAssociation *, 4> laterRefs;

    // 遍历Map中的值 release everything (outside of the lock).
    for (auto &i: refs) {
        // 如果里面包含系统设置的关联值 && 现在是deallocating正在释放，那么把它放到laterRefs
        if (i.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
            // If we are not deallocating, then RELEASE_LATER associations don't get released.
            if (deallocating)
                laterRefs.append(&i.second);
        } else {
            // 我们设置的关联值走这里，对里面的值进行release操作
            i.second.releaseHeldValue();
        }
    }
    // 当对象deallocating试，最后对系统设置的关联值进行release操作
    for (auto *later: laterRefs) {
        later->releaseHeldValue();
    }
}
